//
// Constant Buffer Variables
//

SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

cbuffer cbConstant
{
    float3 vLightDir = float3(-1, -1, 1);
};

cbuffer camera_cbuffer
{
	//transformation
    matrix View;
    matrix Projection;
	float3 EyePos;
};

cbuffer cbChangesEveryFrame
{
    matrix World;
	
	//material
	float3 Kd, Ka, Ks;
	float MaterialAlpha, MaterialSpecularity;
	bool HasDiffuseTexture = false;
	bool HasAlphaTexture = false;
	bool HasSpecularTexture = false;
	bool HasAmbientTexture = false;
};

Texture2D DiffuseTexture;
Texture2D AlphaTexture;
Texture2D AmbientTexture;
Texture2D SpecularTexture;

struct VS_INPUT
{
    float3 Pos          : POSITION;         //position
    float3 Norm         : NORMAL;           //normal
    float2 Tex          : TEXCOORD0;        //texture coordinate
};

struct VS_INPUT_P1T3
{
    float3 Pos          : POSITION;         //position
    float3 Tex          : TEXCOORD0;        //texture coordinate
};

struct VS_INPUT_P1T2
{
    float3 Pos          : POSITION;         //position
    float2 Tex          : TEXCOORD0;        //texture coordinate
};

struct VS_INPUT_P
{
	float3 Pos	: POSITION;
};

struct GS_INPUT
{
	float3 Pos	: POSITION;
	float2 Tex	: TEXCOORD0;
};

struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float3 Norm : TEXCOORD0;
    float2 Tex : TEXCOORD1;
    float3 ViewR : TEXCOORD2;
};

struct PS_INPUT_P
{
	float4 Pos : SV_POSITION;
};

//--------------------------------------------------------------------------------------
// States
//--------------------------------------------------------------------------------------
DepthStencilState EnableDepth
{
    DepthEnable = TRUE;
    DepthWriteMask = ALL;
    DepthFunc = LESS_EQUAL;
};

BlendState NoBlending
{
    AlphaToCoverageEnable = FALSE;
    BlendEnable[0] = FALSE;
};

BlendState AlphaBlending
{
	AlphaToCoverageEnable = FALSE;
	BlendEnable[0] = TRUE;
	SrcBlend = SRC_ALPHA;
	DestBlend = INV_SRC_ALPHA;
	BlendOp = ADD;
	SrcBlendAlpha = SRC_ALPHA;
	DestBlendAlpha = INV_SRC_ALPHA;
	BlendOpAlpha = ADD;
	RenderTargetWriteMask[0] = 0x0F;
};

RasterizerState NoCulling
{
    CullMode = None;
	FillMode = Solid;
};

RasterizerState CullBack
{
    CullMode = Back;
	FillMode = Solid;
};

RasterizerState RSWireFrame
{
    CullMode = Back;
	FillMode = WireFrame;
};

//
// Vertex Shader
//
PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    
    output.Pos = mul( float4(input.Pos,1), World );
    output.Pos = mul( output.Pos, View );
	output.ViewR = output.Pos.xyz;
    output.Pos = mul( output.Pos, Projection );
    output.Norm = mul( input.Norm, (float3x3)World );
	output.Norm = mul( output.Norm, (float3x3)View );
    output.Tex = input.Tex;
    
    return output;
}

GS_INPUT GenNormVS( VS_INPUT_P1T2 input )
{
	GS_INPUT output = (GS_INPUT)0;
	output.Pos = input.Pos;
	output.Tex = input.Tex;
	return output;
}

PS_INPUT_P ColorVS(VS_INPUT_P input)
{
	PS_INPUT_P output = (PS_INPUT_P)0;
	output.Pos = mul( float4(input.Pos,1), World );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
	return output;
}

//
// Geometry Shader
//
[maxvertexcount(3)]
void GenNormGS(triangle GS_INPUT input[3], inout TriangleStream<PS_INPUT> SpriteStream)
{
    PS_INPUT output;
    
	float3 faceEdgeA = input[1].Pos - input[0].Pos;
    float3 faceEdgeB = input[2].Pos - input[0].Pos;
    float3 faceNormal = normalize( cross(faceEdgeA, faceEdgeB) );

    [unroll] for(int i=0; i<3; i++)
    {
		output.Pos = mul( float4(input[i].Pos,1), World );
		output.Pos = mul( output.Pos, View );
		output.ViewR = output.Pos.xyz;
        output.Pos = mul( output.Pos, Projection );
        output.Norm = mul( faceNormal, (float3x3)World );
		output.Norm = mul( output.Norm, (float3x3)View );
		output.Tex = input[i].Tex;
		
        SpriteStream.Append(output);
    }
    SpriteStream.RestartStrip();
}


//
// Pixel Shader
//
float4 PSBlinnPhong(PS_INPUT input) : SV_Target
{
	float3 cAmbient, cDiffuse, cSpecular;
	float4 cDiffuse4C = {0.0f, 0.0f, 0.0f, 0.0f};
	float alpha;

	//OPENGL texcoord orientation
	input.Tex.y = 1 - input.Tex.y;
	
	//ambient color
	if (HasAmbientTexture)
		cAmbient = AmbientTexture.Sample(samLinear, input.Tex).xyz;
	else
		cAmbient = Ka.xyz;

	//diffuse color
	if (HasDiffuseTexture)
	{
		cDiffuse4C = DiffuseTexture.Sample(samLinear, input.Tex);
        cDiffuse = cDiffuse4C.xyz;
    }
	else
		cDiffuse = Kd.xyz;

	//specular color
	if (HasSpecularTexture)
		cSpecular = SpecularTexture.Sample(samLinear, input.Tex).xyz;
	else
		cSpecular = Ks.xyz;

	//specular intensity
	float3 lightV = normalize(vLightDir);
	float3 normV = normalize(input.Norm);
	float3 viewV = normalize(input.ViewR);
	float3 H = -normalize(lightV + viewV);
	float HdotN = saturate( dot(normV, H) );
	float specular = pow(HdotN, MaterialSpecularity);
	
	//diffuse intensity	
	float diffuse = saturate(dot(normV, -lightV));

	//opacaity
	if (HasAlphaTexture)
		alpha = AlphaTexture.Sample(samLinear, input.Tex).x;
	else if (HasDiffuseTexture)
		alpha = min(MaterialAlpha, cDiffuse4C.w);
	else
		alpha = MaterialAlpha;

	if (alpha < 0.01f) discard;
	//return float4(diffuse, diffuse, diffuse, alpha);
	//return float4(input.Tex.xy, 0.0f, alpha);
	//return float4(input.ViewR, 1.0);
	
	//return float4(cDiffuse, 1.0f);
	return float4(cDiffuse * diffuse + cSpecular * specular + cAmbient * 0.3f, alpha);
}


float4 ColorPS(PS_INPUT_P input) : SV_Target
{
	return float4(Kd, 1.0);
}


//
// Technique
//
technique10 Render
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PSBlinnPhong() ) );
        
        SetDepthStencilState( EnableDepth, 0 );
        SetBlendState( AlphaBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
		SetRasterizerState( CullBack );
    }
};

technique10 RenderNoCull
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PSBlinnPhong() ) );
        
        SetDepthStencilState( EnableDepth, 0 );
        SetBlendState( AlphaBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
		SetRasterizerState( NoCulling );
    }
};

technique10 FaceNormal
{
	pass P0
	{
        SetVertexShader( CompileShader( vs_4_0, GenNormVS() ) );
        SetGeometryShader( CompileShader( gs_4_0, GenNormGS() ) );
        SetPixelShader( CompileShader( ps_4_0, PSBlinnPhong() ) );
        
        SetDepthStencilState( EnableDepth, 0 );
        SetBlendState( AlphaBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );		
		SetRasterizerState( CullBack );
	}
};

technique10 Color
{
	pass P0
	{
        SetVertexShader( CompileShader( vs_4_0, ColorVS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, ColorPS() ) );
        
        SetDepthStencilState( EnableDepth, 0 );
        SetBlendState( AlphaBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );		
		SetRasterizerState( NoCulling );
	}
};

